/* SPDX-License-Identifier: GPL-2.0 */
/* interrupt.h */
#ifndef SEMINIX_INTERRUPT_H
#define SEMINIX_INTERRUPT_H

#include <utils/compiler.h>
#include <seminix/cache.h>
#include <seminix/percpu.h>
#include <seminix/smp.h>
#include <seminix/preempt.h>
#include <seminix/irqflags.h>

struct tcb;

/*
 * We want to know which function is an entrypoint of a hardirq or a softirq.
 */
#define __irq_entry		 __attribute__((__section__(".irqentry.text")))

/**
 * enum irqreturn
 * @IRQ_NONE		interrupt was not from this device or was not handled
 * @IRQ_HANDLED		interrupt was handled by this device
 * @IRQ_WAKE_THREAD	handler requests to wake the handler thread
 */
enum irqreturn {
    IRQ_NONE		= (0 << 0),
    IRQ_HANDLED		= (1 << 0),
    IRQ_IPC		    = (1 << 1),
};
typedef enum irqreturn irqreturn_t;

typedef irqreturn_t (*irq_handler_t)(int, void *);

struct irqaction {
    irq_handler_t       handler;
    void            *dev_id;
    void __percpu   *percpu_dev_id;
    struct irqaction    *next;
    struct tcb          *thread;
    int             irq;
    unsigned int    flags;
    const char      *name;
} ____cacheline_internodealigned_in_smp;

/*
 * These correspond to the IORESOURCE_IRQ_* defines in
 * linux/ioport.h to select the interrupt line behaviour.  When
 * requesting an interrupt without specifying a IRQF_TRIGGER, the
 * setting should be assumed to be "as already configured", which
 * may be as per machine or firmware initialisation.
 */
#define IRQF_TRIGGER_NONE	0x00000000
#define IRQF_TRIGGER_RISING	0x00000001
#define IRQF_TRIGGER_FALLING	0x00000002
#define IRQF_TRIGGER_HIGH	0x00000004
#define IRQF_TRIGGER_LOW	0x00000008
#define IRQF_TRIGGER_MASK	(IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)

#define IRQF_SHARED		0x00000080
#define IRQF_TIMER		0x00000200
#define IRQF_PERCPU		0x00000400

static inline void irq_enter(void)
{
    preempt_count_add(HARDIRQ_OFFSET);
}

static inline void irq_exit(void)
{
    preempt_count_sub(HARDIRQ_OFFSET);
}

static inline void nmi_enter(void)
{
    BUG_ON(in_nmi());
    preempt_count_add(NMI_OFFSET + HARDIRQ_OFFSET);
}

static inline void nmi_exit(void)
{
    BUG_ON(!in_nmi());
    preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET);
}

#endif /* !SEMINIX_INTERRUPT_H */
